<?php
/**
 *  后台店铺Store商品的管理
 * Author: lyun
 * Date: 2018/3/30
 * Created by Panxsoft.
 */


namespace App\Http\Controllers\Admin\Store\Product;

use App\Http\Controllers\ApiController;
use App\Http\Requests\Admin\Customer\Product\ProductStatusRequest;
use App\Http\Requests\Admin\Panx\Product\ProductCreateInfoRequest;
use App\Http\Requests\Admin\Store\ProductStoreRequest;
use App\Http\Requests\Admin\Store\ProductUpdateRequest;
use App\Http\Requests\Admin\Store\StorePivotCustomerProductUpdate;
use App\Models\Admin\CosmeticMaterial;
use App\Models\Admin\StoreAdmin;
use App\Models\Customer\Asset;
use App\Models\Panx\BodyPosition;
use App\Models\Panx\Brand;
use App\Models\Panx\BrandSeries;
use App\Models\Panx\Category;
use App\Models\Panx\Product;
use App\Models\Panx\ProductMod;
use App\Models\Panx\ProductSku;
use App\Models\Panx\ProductUrlType;
use App\Models\Panx\Sex;
use App\Models\Store\StorePivotCustomerProduct;
use App\Services\ExcelExporters\ProductExporter;
use App\Services\Locale\Language;
use App\Models\Customer\Language as ModelLanguage;
use App\Services\Repositories\AssetRepository;
use App\Services\Repositories\Product\ProductRepository;
use App\Services\Repositories\Product\ProductTagRepository;
use App\Services\Utils\Guard;
use App\Services\Utils\RequestParam;
use App\Services\Utils\Transaction;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Http\Request;
use App\Http\Requests\Admin\Store\ProductIndexRequest;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;

class ProductController extends ApiController
{
    const ROUTES = [
        'index'                    => 'admin.store.product.index',
        'create'                   => 'admin.store.product.create',
        'store'                    => 'admin.store.product.store',
        'update'                   => 'admin.store.product.update',
        'destroy'                  => 'admin.store.product.destroy',
        'patchStatus'              => 'admin.store.product.patch-status',
        'updateStorePivotCustomer' => 'admin.store.product.store-pivot-customer',
    ];

    protected $productRepository;

    public function __construct(ProductRepository $productRepository)
    {
        $this->productRepository = $productRepository;
    }

    /**
     * 店铺和客户的商品列表
     * @test
     *
     * @param ProductIndexRequest $request
     * @param ProductRepository $productRepository
     * @return \Illuminate\Http\Response
     * @throws \Exception
     *
     */
    public function index(ProductIndexRequest $request, ProductRepository $productRepository)
    {
        // 超管才能操作的
        $this->checkIsTrue(StoreAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        $store = Guard::store();

        // 店铺和客户的商品信息
        /** @var LengthAwarePaginator|Collection $products */
        $products = $productRepository->adminStoreProductsQuery($store, $request)
            ->paginate($request->per_page ?: 15);

        $this->productRepository->transformProducts($products);

        return $this->response(compact('products'));
    }

    /**
     * 导出店铺商品
     *
     * @param ProductIndexRequest $request
     * @param ProductRepository $productRepository
     * @throws \Exception
     * @throws \Maatwebsite\Excel\Exceptions\LaravelExcelException
     */
    public function exportStoreProduct(ProductIndexRequest $request, ProductRepository $productRepository)
    {
        // 超管才能操作的
        $this->checkIsTrue(StoreAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        $store = Guard::store();

        $products = $productRepository->adminStoreProductsQuery($store, $request)
            ->get();

        (new ProductExporter)->export($products, trans('admin.store_product'));
    }

    /**
     * Show the form for creating a new resource.
     * @test
     *
     * @param AssetRepository $assetRepo
     * @param ProductTagRepository $productTagRepo
     *
     * @return \Illuminate\Http\Response
     */
    public function create(AssetRepository $assetRepo,
                           ProductTagRepository $productTagRepo)
    {
        $customer_id = Guard::store()->customer_id;

        // 客户开通的商品类别
        $language_code = app()->getLocale();
        $language_id = ModelLanguage::mapCodeToId($language_code);

        $customer = Guard::store()->customer;
        $categories = $customer->getCustomerCategories($language_id);
        $group_categories = Category::groupCategoryByCustomType($categories);

        $brandModel = new Brand;

        $brands = $brandModel
            ->where('customer_id', $customer_id)
            ->latest()
            ->pluck('name', 'id');

        $brand_with_series = $brandModel
            ->where('customer_id', $customer_id)
            ->latest()
            ->select('id','name')
            ->with('brandSeries')
            ->get();

        $product_tags = $productTagRepo->getProductTags();

        $assets = (new Asset)
            ->filterStoreAndCustomer(Guard::store()->id, $customer_id)
            ->latest()
            ->get();

        $assets_group = $assetRepo->groupAssetsByCategory($assets);

        $product_url_types = (new ProductUrlType)->latest('priority')->get(['id', 'const', Language::getLocalNameColumn()]);

        $sexes = Sex::all(['id', Language::getLocalNameColumn()]);

        $body_positions = BodyPosition::all(['id', Language::getLocalNameColumn()]);

        /** @deprecated (暂时不用了) */
        $cosmetic_materials = CosmeticMaterial::filterCustomer($customer_id)->get(['id', 'name']);

        return $this->response(compact(
            'categories', 'brands', 'product_tags',
            'assets', 'assets_group', 'product_url_types',
            'sexes', 'body_positions', 'cosmetic_materials',
            'group_categories','brand_with_series'
        ));
    }

    /**
     *  Store上传商品信息
     * Store a newly created resource in storage.
     * @test
     *
     * @param  ProductStoreRequest $request
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function store(ProductStoreRequest $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(StoreAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        $product_detail = $this->productRepository->assembleProductDetail($request, Guard::store()->customer_id, Guard::store()->id);

        $stored = Transaction::handle(function () use ($request, $product_detail) {

            /** @var Product $product */
            $product = Product::create($product_detail);

            // 保存商品关联部位信息
//            $this->productRepository->syncProductPivotBodyPosition($product, $request->body_position_ids);

            // 关联商品标签的信息
            $this->productRepository->syncProductTags($product, $request->product_tag_ids);

            // 商品缩略图的上传
            $product->updateThumb($request->thumb);

            // 商品颜色缩略图的上传
            $product->updateColorThumb($request->color_thumb);

            // 商品属性值,商品规格值，商品sku值 的保存
//            $this->storeProductPivotAttributeValue($request, $product);
//            $this->storeProductPivotSpecValue($request, $product);
//            $this->storeProductSku($request, $product);

            return $product;
        });

        return $this->response(compact('stored'));
    }


    /**
     * Update the specified resource in storage.
     * @test
     *
     * @param  ProductUpdateRequest $request
     * @param  int $id
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function update(ProductUpdateRequest $request, $id)
    {
        // 超管才能操作的
        $this->checkIsTrue(StoreAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        /** @var Product $product */
        $product = Product::where('customer_id', Guard::store()->customer_id)
            ->where('store_id', Guard::storeAdmin()->store_id)
            ->find($id);
        $this->checkItemExist($product, trans('admin.product'));

        $product_detail = $this->productRepository->assembleProductDetail($request, Guard::store()->customer_id, Guard::store()->id);

        $product_detail = $this->productRepository->unsetUpdateProductDetail($product, $product_detail);

        $store_pivot_customer_product = DB::table('store_pivot_customer_product')->where([
            ['store_id', Guard::store()->id],
            ['product_id', $id],
        ]);

        $updated = Transaction::handle(function () use ($request, $product_detail, $product, $store_pivot_customer_product) {

            $product->update($product_detail);

            // 保存商品关联部位信息
//            $this->productRepository->syncProductPivotBodyPosition($product, $request->body_position_ids);

            // 商品缩略图的上传
            $product->updateThumb($request->thumb);

            // 商品颜色缩略图的上传
            $product->updateColorThumb($request->color_thumb);

            // 商品标签的更改
            $this->productRepository->syncProductTags($product, $request->product_tag_ids);

            // 商品属性值，规格值，sku的更改
//            $this->storeProductPivotAttributeValue($request, $product);
//            $this->storeProductPivotSpecValue($request, $product);
//            $this->storeProductSku($request, $product);

            return true;
        });

        return $this->response(compact('updated'));
    }

    /**
     * 更新 store_pivot_customer_product 优先级， 与上下架操作
     * @test
     *
     * @param $request
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function updateStorePivotCustomerProduct(StorePivotCustomerProductUpdate $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(StoreAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        /** @var Product $customer_product */
        $customer_product = Product::find($request->product_id);
            $this->checkItemExist($customer_product, trans('admin.product'));
            $this->checkCustomerProduct($customer_product);

        $update_or_create = (new StorePivotCustomerProduct)->updateOrCreate(
            [
                'product_id' => $request->product_id,
                'store_id'   => Guard::store()->id,
            ],
            RequestParam::normalize(['is_sale', 'priority'])
        );

        return $this->response(compact('update_or_create'));
    }

    /**
     * Remove the specified resource from storage.
     * @test
     *
     * @param  int $id
     * @return \Illuminate\Http\Response
     * @throws \Exception
     */
    public function destroy($id)
    {
        // 超管才能操作的
        $this->checkIsTrue(StoreAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        /** @var Product $product */
        $product = (new Product)
            ->where('customer_id', Guard::store()->customer_id)
            ->where('store_id', Guard::storeAdmin()->store_id)
            ->find($id);
        $this->checkItemExist($product, trans('admin.product'));

        $deleted = Transaction::handle(function () use ($product) {
            return ProductRepository::destroyCleanRelations($product);
        });

        return $this->response(compact('deleted'));
    }

    /**
     * 自定义修改商品的 [type => value] 信息
     * @test
     *
     * @param ProductStatusRequest $request
     * @return array|\Illuminate\Http\Response
     * @throws \Exception
     */
    public function patchStatus(ProductStatusRequest $request)
    {
        // 超管才能操作的
        $this->checkIsTrue(StoreAdmin::checkIsSuperMan(), trans('admin.is_super_man'));

        $type = $request->type;
        $value = $request->value;

        /** @var Product $product */
        $product = Product::where('store_id', Guard::storeAdmin()->store_id)
            ->where('customer_id', Guard::store()->customer_id)
            ->find($request->id);
        $this->checkItemExist($product, trans('admin.product'));

        $this->productRepository->checkSaleCondition($product);

        $updated = $product->update([$type => $value]);

        return $this->response(compact('updated'));
    }

    /**
     * @deprecated (暂时不用了！)
     *  返回 categories 或者 product_model 关联的 attributeValues 和 specValurs 的数据
     *
     * @param ProductCreateInfoRequest $request
     * @return \Illuminate\Http\Response
     */
    public function createInfo(ProductCreateInfoRequest $request)
    {
        switch ($request->type) {
            case 'category':
                $data = Category::where('parent_id', $request->id)->pluck('name', 'id')->all();
                break;
            case 'product_model':
                $data = $this->getSpecsAndAttributes($request->id);
                break;
            default:
                $data = false;
        }

        return $this->response(compact('data'));
    }

    /**
     * @param ProductStoreRequest|ProductUpdateRequest|Request $request
     * @param Product $product
     */
    private function storeProductPivotSpecValue(Request $request, Product $product)
    {
        $spec_value_ids = data_get($request->product_skus, '*.spec_value_ids');

        $spec_value_ids = collect($spec_value_ids)->collapse()->unique()->all();

        $product->specValues()->sync($spec_value_ids);
    }

    /**
     * @param ProductStoreRequest|ProductUpdateRequest|Request $request
     * @param Product $product
     * @return bool
     */
    private function storeProductPivotAttributeValue(Request $request, Product $product)
    {
        $req_attr_vals = $request->attribute_values;
        if (is_null($req_attr_vals)) {
            return false;
        }

        $attr_val_ids = array_column($req_attr_vals, 'id');

        $product->attributeValues()->sync($attr_val_ids);
    }

    /**
     * @param ProductStoreRequest|ProductUpdateRequest|Request $request
     * @param Product $product
     * @return bool
     */
    private function storeProductSku(Request $request, Product $product)
    {
        if (is_null($request->product_skus)) return false;

        list(
            $new_skus,
            $update_skus,
            $delete_sku_ids
            ) = $this->normalizeUpdateSkuData($request->product_skus, $product);

        $this->updateInsertSkus($new_skus);

        $this->updateExistSkus($update_skus);

        $this->updateDeleteSkus($delete_sku_ids);

        return true;
    }

    /**
     * @param array $request_skus
     * @param Product $product
     * @return array
     */
    private function normalizeUpdateSkuData(array $request_skus, Product $product)
    {
        $new_skus = [];
        $update_skus = [];

        $request_skus = $this->normalizeRequestSkus($request_skus);

        /** @var ProductSku[]|Collection $exist_skus */
        $exist_skus = $product->productSkus;

        foreach ($request_skus as $product_sku) {
            $store_sku = [
                'spec_value_ids' => json_encode($product_sku['spec_value_ids']),
                'product_id'     => $product->id,
                'sell_price'     => $product_sku['sell_price'] ?? 0,
                'thumb'          => $product_sku['thumb'] ?? '',
                'sku_code'       => $product_sku['sku_code'] ?? '',
            ];

            if ($exist_skus->where('spec_value_ids', $product_sku['spec_value_ids'])->first()) {
                array_push($update_skus, $store_sku);
            } else {
                array_push($new_skus, $store_sku);
            }
        }

        /** @var array $req_spec_value_ids */
        $req_spec_value_ids = array_column($request_skus, 'spec_value_ids');
        $delete_sku_ids = $exist_skus->whereNotIn('spec_value_ids', $req_spec_value_ids)
            ->pluck('id')->all();

        return [$new_skus, $update_skus, $delete_sku_ids];
    }

    /**
     * 把提交的 SKU 中 spec_value_ids 数组内容转成 int 并排序
     * @param array|null $product_skus
     * @return array
     */
    private function normalizeRequestSkus(array $product_skus): array
    {
        return array_map(function (array $product_sku) {
            // id string to int
            $product_sku['spec_value_ids'] = array_map(function ($id) {
                return (int)$id;
            }, $product_sku['spec_value_ids']);

            // sort ids by ascend
            $product_sku['spec_value_ids'] = array_values(array_sort($product_sku['spec_value_ids']));

            return $product_sku;
        }, $product_skus);
    }

    /**
     * @param array $new_skus
     */
    private function updateInsertSkus(array $new_skus)
    {
        if (!count($new_skus)) return;

        ProductSku::insert($new_skus);
    }

    /**
     * @param array $update_skus
     */
    private function updateExistSkus(array $update_skus)
    {
        foreach ($update_skus as $update_sku) {
            ProductSku::where([
                'spec_value_ids' => $update_sku['spec_value_ids'],
                'product_id'     => $update_sku['product_id'],
            ])
                ->update($update_sku);
        }
    }

    /**
     * @param array $delete_sku_ids
     */
    private function updateDeleteSkus(array $delete_sku_ids)
    {
        if (!count($delete_sku_ids)) return;

        ProductSku::destroy($delete_sku_ids);
    }

    /**
     * @param $id
     * @return Collection
     */
    private function getSpecsAndAttributes($id)
    {
        /** @var ProductMod $product_model */
        $product_model = ProductMod::find($id); // 前端检测过了

        /** @var Collection $data */
        $data = $product_model->load('specs.specValues', 'attributes.attributeValues');

        return $data;
    }

    /**
     * @param Product $customer_product
     * @throws \App\Exceptions\ApiAbstractException
     */
    private function checkCustomerProduct(Product $customer_product)
    {
        $is_validate_customer_product = ($customer_product->customer_id === Guard::store()->customer_id)
            && ($customer_product->store_id === Product::STORE_ID_CUSTOMER);

        $this->checkIsTrue($is_validate_customer_product, trans('admin.is_validate_customer'));
    }
}

